package odfconverter;

import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Stack;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import odfconverter.AbstractPostProcessor.Element;
import odfconverter.AbstractPostProcessor.Node;

import org.apache.commons.lang.StringUtils;

public class OoxCharactersPostProcessor extends AbstractPostProcessor {

    private final static String NAMESPACE = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
    private String[] RUN_PROPERTIES = { "ins", "del", "moveFrom", "moveTo", "rStyle", "rFonts", "b", "bCs", "i", "iCs", "caps", "smallCaps", "strike", "dstrike", "outline", "shadow", "emboss", "imprint", "noProof", "snapToGrid", "vanish", "webHidden", "color", "spacing", "w", "kern", "position", "sz", "szCs", "highlight", "u", "effect", "bdr", "shd", "fitText", "vertAlign", "rtl", "cs", "em", "lang", "eastAsianLayout", "specVanish", "oMath", "rPrChange" };

    private static char[] PONCTUATION = { '\u002E', '\u0021', '\u003F',  '\u002C', '\u003A', '\u003B', '\u0028', '\u0029', '\u002D' };

    // series of symbol that do not use reverse direction
    private static char[] HEBREW_DIRECT_SYMBOLS = 
        { '\u05B0', '\u05B1', '\u05B2', '\u05B3', '\u05B4', '\u05B5', '\u05B6', '\u05B7', '\u05B8' };

    private static char[] HEBREW_SYMBOLS = 
        { '\u05B9', '\u05BA', '\u05BB', '\u05BC', '\u05BD', '\u05BE', '\u05BF',
		'\u05C0', '\u05C1', '\u05C2', '\u05C3',
		'\u05D0', '\u05D1', '\u05D2', '\u05D3', '\u05D4', '\u05D5', '\u05D6', '\u05D7', '\u05D8', '\u05D9', '\u05DA', '\u05DB', '\u05DC', '\u05DD', '\u05DE', '\u05DF',
		'\u05E0', '\u05E1', '\u05E2', '\u05E3', '\u05E4', '\u05E5', '\u05E6', '\u05E7', '\u05E8', '\u05E9', '\u05EA',
		'\u05F0', '\u05F1', '\u05F2', '\u05F3', '\u05F4' };

    private static char[] ARABIC_BASIC_SYMBOLS = 
        { '\u060C', '\u061B', '\u061F',
		'\u0621', '\u0622', '\u0623', '\u0624', '\u0625', '\u0626', '\u0627', '\u0628', '\u0629', '\u062A', '\u062B', '\u062C', '\u062D', '\u062E', '\u062F',
		'\u0630', '\u0631', '\u0632', '\u0633', '\u0634', '\u0635', '\u0636', '\u0637', '\u0638', '\u0639', '\u063A',
		'\u0640', '\u0641', '\u0642', '\u0643', '\u0644', '\u0645', '\u0646', '\u0647', '\u0648', '\u0649', '\u064A', '\u064B', '\u064C', '\u064D', '\u064E', '\u064F',
		'\u0650', '\u0651', '\u0652', '\u0653', '\u0654', '\u0655' };

    private static char[] ARABIC_EXTENDED_SYMBOLS = 
        { '\u0660', '\u0661', '\u0662', '\u0663', '\u0664', '\u0625', '\u0666', '\u0667', '\u0668', '\u0669', '\u066A', '\u066B', '\u066C', '\u066D', '\u066E', '\u066F',
		'\u0670', '\u0641', '\u0672', '\u0673', '\u0674', '\u0675', '\u0676', '\u0677', '\u0678', '\u0679', '\u067A', '\u067B', '\u067C', '\u067D', '\u067E', '\u067F',
		'\u0680', '\u0681', '\u0682', '\u0683', '\u0684', '\u0685', '\u0686', '\u0687', '\u0688', '\u0689', '\u068A', '\u068B', '\u068C', '\u068D', '\u068E', '\u068F',
		'\u0690', '\u0691', '\u0692', '\u0693', '\u0694', '\u0695', '\u0696', '\u0697', '\u0698', '\u0699', '\u069A', '\u069B', '\u069C', '\u069D', '\u069E', '\u069F',
		'\u06A0', '\u06A1', '\u06A2', '\u06A3', '\u06A4', '\u06A5', '\u06A6', '\u06A7', '\u06A8', '\u06A9', '\u06AA', '\u06AB', '\u06AC', '\u06AD', '\u06AE', '\u06AF',
		'\u06B0', '\u06B1', '\u06B2', '\u06B3', '\u06B4', '\u06B5', '\u06B6', '\u06B7', '\u06B8', '\u06B9', '\u06BA', '\u06BB', '\u06BC', '\u06BD', '\u06BE', '\u06BF',
		'\u06C0', '\u06C1', '\u06C2', '\u06C3', '\u06C4', '\u06C5', '\u06C6', '\u06C7', '\u06C8', '\u06C9', '\u06CA', '\u06CB', '\u06CC', '\u06CD', '\u06CE', '\u06CF',
		'\u06D0', '\u06D1', '\u06D2', '\u06D3', '\u06D4', '\u06D5', '\u06D6', '\u06D7', '\u06D8', '\u06D9', '\u06DA', '\u06DB', '\u06DC', '\u06DD', '\u06DE', '\u06DF',
		'\u06E0', '\u06E1', '\u06E2', '\u06E3', '\u06E4', '\u06E5', '\u06E6', '\u06E7', '\u06E8', '\u06E9', '\u06EA', '\u06EB', '\u06EC', '\u06ED',
		'\u06F0', '\u06F1', '\u06F2', '\u06F3', '\u06F4', '\u06F5', '\u06F6', '\u06F7', '\u06F8', '\u06F9', '\u06FA', '\u06FB', '\u06FC', '\u06FD', '\u06FE' };

    private Stack currentNode;
    private Stack store;
    private boolean isRightToLeft;
    
    private static DecimalFormat df = new DecimalFormat("0.##");
    
	protected OoxCharactersPostProcessor(XmlWriter nextWriter) throws Exception {
		super(nextWriter);
        this.currentNode = new Stack();
        this.store = new Stack();
	}

	@Override
	public void WriteEndAttribute() {
        if (InRun())
        {
            EndStoreAttribute();
        }
        else
        {
            this.nextWriter.WriteEndAttribute();
        }
        this.currentNode.pop();
	}

	@Override
	public void WriteEndElement() {
        if (IsRun())
        {
            WriteStoredRun();
        }
        if (InRun())
        {
            EndStoreElement();
        }
        else
        {
            this.nextWriter.WriteEndElement();
        }
        this.currentNode.pop();

	}

	@Override
	public void WriteStartAttribute(String prefix, String localName, String ns) {
        this.currentNode.push(new Attribute(prefix, localName, ns));

        if (InRun())
        {
            StartStoreAttribute();
        }
        else
        {
            this.nextWriter.WriteStartAttribute(prefix, localName, ns);
        }
	}

	@Override
	public void WriteStartElement(String prefix, String localName, String ns) {
        Element e = null;
        if (NAMESPACE.equals(ns) && "r".equals(localName))
        {
            e = new Run(prefix, localName, ns);
        }
        else
        {
            e = new Element(prefix, localName, ns);
        }

        this.currentNode.push(e);

        if (InRun())
        {
            StartStoreElement();
        }
        else
        {
            this.nextWriter.WriteStartElement(prefix, localName, ns);
        }
	}

	@Override
	public void WriteString(String text) {
        if (InRun())
        {
            StoreString(text);
        }
        else if (text.contains("cxnSp"))
        {
           this.nextWriter.WriteString(EvalExpression(text));
        }
        // added by vipul for Shape Rotation
        //Start
        else if (text.contains("draw-transform"))
        {
            this.nextWriter.WriteString(EvalRotationExpression(text));
        }
        else if (text.contains("group-svgXYWidthHeight"))
        {
            this.nextWriter.WriteString(EvalGroupExpression(text));
        }
        //End 
        //Shadow calculation
        else if (text.contains("a-outerShdw-dist") || text.contains("a-outerShdw-dir"))
        {
            this.nextWriter.WriteString(EvalShadowExpression(text));
        }
        // This condition is to check for hyperlink relative path 
        else if (text.contains("hyperlink-path"))
        {
            this.nextWriter.WriteString(EvalHyperlinkPath(text));
        }
        //Image Cropping Calculation Added by Sonata-15/11/2007
        else if (text.contains("image-properties"))
        {
            EvalImageCropping(text);
        }
        //Callout Adjustments Calculation Added by Sonata
        else if (text.contains("Callout-DirectAdj") || text.contains("Callout-DirectLine"))
        {
            this.nextWriter.WriteString(EvalCalloutAdjsExpn(text));
        }
        else if (IsDate())
        {
            {
                //DateTime dateTime = DateTime.Parse(text, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal);
                //this.nextWriter.WriteString(dateTime.ToUniversalTime().ToString("s") + "Z");
                DateFormat df = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
             // explicitly set timezone of input if needed
                df.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
                java.util.Date dateTime ;
				try {
					dateTime = df.parse(text);
					this.nextWriter.WriteString(df.format(dateTime));
				} catch (ParseException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}                
            }
        }
        else
        {
            this.nextWriter.WriteString(text);
        }

	}

    /*
     * General methods
     * Private method to evaluate an expression with trignametric functions.
     */
    private String EvalExpression(String text)
    {
        String[] arrVal =  text.split(":");
       
        String attVal = "";
        if (arrVal.length >= 6)
        {
            double x1 = Double.parseDouble(arrVal[2]);
            double x2 = Double.parseDouble(arrVal[3]);
            double y1 = Double.parseDouble(arrVal[4]);
            double y2 = Double.parseDouble(arrVal[5]);

            double xCenter = (x1 + x2) * 360000 / 2;
            double yCenter = (y1 + y2) * 360000 / 2;

            double angle, x, y;
            int flipH = 0;
            int flipV = 0;

            angle = Math.atan2((y2 - y1), (x2 - x1));

            double angleRd = angle / Math.PI * 180;

            if (angleRd < 0)
            {
                angleRd += 360;
            }

            int sector = (int)(angleRd / 45) + 1;

            if ((sector == 1) || (sector == 8))
            {
                angle = 0;
            }
            else if ((sector == 2) || (sector == 6))
            {
                angle = 270;
            }
            else if ((sector == 3) || (sector == 7))
            {
                angle = 90;
            }
            else if ((sector == 4) || (sector == 5))
            {
                angle = 180;
            }

            if (arrVal[1].equals("rot"))
            {

                attVal = df.format(angle * 60000);
            }

            if ((sector == 2) || (sector == 7))
            {
                flipH = 1;
            }

            if ((sector == 4) || (sector == 6) || (sector == 7) || (sector == 8))
            {
                flipV = 1;
            }

            if (arrVal[1].equals( "flipH"))
            {
                attVal = df.format(flipH);
            }
            if (arrVal[1].equals("flipV"))
            {
                attVal = df.format(flipV);
            }

            angleRd = angle / 180 * Math.PI;

            double cxby2 = (Math.cos(angleRd) * (x2 - x1) + Math.sin(angleRd) * (y2 - y1)) / 2 * 360000;
            double cyby2 = (Math.sin(angleRd) * (x1 - x2) + Math.cos(angleRd) * (y2 - y1)) / 2 * 360000;
            double cx = 2 * Math.round(cxby2);
            double cy = 2 * Math.round(cyby2);
            if (flipH == 1)
            {
                cx = -1 * cx;
            }
            if (flipV == 1)
            {
                cy = -1 * cy;
            }
            if (arrVal[1].equals( "cx"))
            {
                if (text.contains("true"))
                    cx =Math.round( cx / 1588);
                attVal = String.format("%.0f", cx);
            }
            if (arrVal[1].equals( "cy"))
            {
                if (text.contains("true"))
                    cy =  Math.round( cy / 1588);
                attVal = String.format("%.0f", cy);
            }
            if (arrVal[1].equals( "x"))
            {
                x = Math.round(xCenter - cx / 2);
                if (text.contains("true"))
                    x = Math.round(x / 1588);
                attVal =String.format("%.0f", x);
            }
            if (arrVal[1].equals("y"))
            {
                y = Math.round(yCenter - cy / 2);
                if (text.contains("true"))
                    y = Math.round(y / 1588);
                attVal = String.format("%.0f", y);

            }
        }
        return attVal;
    }
    // added by vipul for Shape Rotation
    //Start
    private String EvalRotationExpression(String text)
    {
        String[] arrVal = new String[7];
       
        String strReturn="";
        arrVal = text.split(":");
        String strXY = arrVal[1];
        Double dblRadius = 0.0;
        Double dblXc = 0.0;
        Double dblYc = 0.0;
        Double dblalpha = 0.0;
        Double dblbeta = 0.0;
        Double dblgammaDegree = 0.0;
        Double dblgammaR = 0.0;
        Double dblX2 = 0.0;
        Double dblY2 = 0.0;
        Double centreX = 0.0;
        Double centreY = 0.0;
        Double dblRotation = 0.0;
       
        if (arrVal.length == 7)
        {
            double arrValueWidth = Double.parseDouble(arrVal[2]);
            double arrValueHeight = Double.parseDouble(arrVal[3]);
            double arrValueX2 = Double.parseDouble(arrVal[4]);
            double arrValueY2 = Double.parseDouble(arrVal[5]);
            double arrValueAngle = Double.parseDouble(arrVal[6]);

            if (arrVal[0].contains("draw-transform"))
            {
                
                centreX = 360000.00 * arrValueWidth;
                centreY = 360000.00 * arrValueHeight;

                dblRadius = Math.sqrt(centreX * centreX + centreY * centreY) / 2.0;

                if (Math.abs(centreY / 2) > 0)
                {
                dblbeta =  Math.atan( Math.abs(centreX/2) / Math.abs(centreY/2) )* 180.00 / Math.PI;
                 }
                dblalpha = -180.00 * arrValueAngle / Math.PI;

                if (Math.abs(dblbeta - dblalpha) > 0)
                {
                dblgammaDegree = (dblbeta - dblalpha) % ((int)((dblbeta - dblalpha) / Math.abs(dblbeta - dblalpha)) * 360) + 90;
                }
                dblgammaR = (360.00 - dblgammaDegree) / 180.00 * Math.PI;
                dblXc = (arrValueX2 * 360036.00) - (dblRadius * Math.cos(dblgammaR));
                dblYc = (arrValueY2 * 360036.00) - (dblRadius * Math.sin(dblgammaR));

                dblX2 = dblXc - centreX / 2.0;
                dblY2 = dblYc - centreY / 2.0;
                dblRotation = new Double((int)Math.round(-1 * (arrValueAngle * 180.00 / Math.PI ) * 60000.00 )).doubleValue();
              
                    }

        }
        if (strXY.contains("XGroup"))
            dblX2 = dblX2 / 1588;
       
        if (strXY.contains("YGroup"))
            dblY2 = dblY2 / 1588;

        if (strXY.contains("X"))
        {

            strReturn = String.valueOf((int)Math.round(dblX2));

        }
        if (strXY.contains("Y"))
        {
            strReturn = String.valueOf((int)Math.round(dblY2));

        }
        if (strXY.contains("ROT"))
        {
            strReturn = String.format("%.0f", dblRotation);

        }
        return strReturn;
    }
    private String EvalGroupExpression(String text)
    {
       
        int intCount;
        int intNextCnt;
        double tempVal;
        double dblX;
        double dblY;
        double dblWidth;
        double dblHeight;
        double dblRot;
      
        double dblValueNext;

        ArrayList arrListX = new ArrayList();
        ArrayList arrListY = new ArrayList();

        String strReturn = "";
        String[] arrVal = text.split("@",-1);

        if (arrVal[1] == "")
            return "0";
      
        for (intCount = 1; intCount < arrVal.length - 1; intCount++)
        {
             String[] arrCords = arrVal[intCount].split(":");
      
            if (arrCords[0].equals(""))
                arrCords[0] = "0";
            if (arrCords[1].equals(""))
                arrCords[1] = "0";
            if (arrCords[2].equals(""))
                arrCords[2] = "0";
            if (arrCords[3].equals(""))
                arrCords[3] = "0";
            if (arrCords[4].equals(""))
                arrCords[4] = "0";

             dblX = Double.parseDouble(arrCords[0]);
             dblY = Double.parseDouble(arrCords[1]);
             dblWidth = Double.parseDouble(arrCords[2]);
             dblHeight = Double.parseDouble(arrCords[3]);
             dblRot = Double.parseDouble(arrCords[4]);
       
             tempVal = dblX;

             arrListX.add(tempVal);

             tempVal = tempVal + Math.cos(dblRot) * dblWidth;
            arrListX.add(tempVal);

            tempVal = tempVal + Math.sin(dblRot) * dblHeight;
            arrListX.add(tempVal);

            tempVal = dblX + Math.sin(dblRot) * dblHeight;
            arrListX.add(tempVal);

    
            tempVal = dblY;
            arrListY.add(tempVal);

            tempVal = tempVal - Math.sin(dblRot) * dblWidth;
            arrListY.add(tempVal);

            tempVal = tempVal + Math.cos(dblRot) * dblHeight;
            arrListY.add(tempVal);

            tempVal = dblY + Math.cos(dblRot) * dblHeight;
            arrListY.add(tempVal);

          }
       
        String strXY = arrVal[0];

        double maxX = 0;
        double maxY = 0; 

        double minX = 0; 
        double minY = 0;

        long ChOffX;
        long ChOffY;
        long ChExtX;
        long ChExtY;
        long OffX;
        long OffY;
        long ExtX;
        long ExtY;


             minX =Double.parseDouble(arrListX.get(0).toString());
             maxX = Double.parseDouble(arrListX.get(0).toString());

             maxY = Double.parseDouble(arrListY.get(0).toString());
             minY = Double.parseDouble(arrListY.get(0).toString());
             for (intNextCnt = 1; intNextCnt < arrListX.size() - 1; intNextCnt++)
             {
                 dblValueNext = Double.parseDouble(arrListX.get(intNextCnt).toString());

                if (dblValueNext <= minX)
                {
                   minX = dblValueNext;
                  }
                  if (dblValueNext >= maxX)
                  {
                      maxX = dblValueNext;
                }
            }

           
               for (intNextCnt = 1; intNextCnt < arrListY.size() - 1; intNextCnt++)
               {
                   dblValueNext = Double.parseDouble(arrListY.get(intNextCnt).toString());

                   if (dblValueNext <= minY)
                {
                       minY = dblValueNext;
                   }
                   if (dblValueNext >= maxY)
                   {
                       maxY = dblValueNext;
                }

            }
             //Bounding Box
               OffX = (long) Math.round(minX * 360000);
               OffY = (long)Math.round(minY * 360000);
               ExtX = (long)Math.round((Math.abs(maxX - minX)) * 360000);
               ExtY = (long)Math.round(Math.abs((maxY - minY)) * 360000);

             //Child Bounding Box
               ChOffX = OffX / 1588;
               ChOffY = OffY / 1588;
               ChExtX = ExtX / 1588;
               ChExtY = ExtY / 1588;

               if (strXY.contains("InnerGroup"))
               {
                   if (strXY.contains("onlyX"))
                strReturn = String.valueOf(ChOffX);
                   else if (strXY.contains("onlyY"))
                strReturn = String.valueOf( ChOffY);
                   else if (strXY.contains("onlyCX"))
                strReturn = String.valueOf(ChExtX);
                   else if (strXY.contains("onlyCY"))
                strReturn = String.valueOf(ChExtY);
                   else if (strXY.contains("onlyChX"))
                strReturn = String.valueOf(ChOffX);
                   else if (strXY.contains("onlyChY"))
                strReturn = String.valueOf(ChOffY);
                   else if (strXY.contains("onlyChCX"))
                strReturn = String.valueOf(ChExtX);

                   else if (strXY.contains("onlyChCY"))
                strReturn = String.valueOf(ChExtY);



               }
               else
               {

                   if (strXY.contains("onlyX"))
                strReturn = String.valueOf(OffX);
                   else if (strXY.contains("onlyY"))
                strReturn = String.valueOf( OffY);
                   else if (strXY.contains("onlyCX"))
                strReturn = String.valueOf( ExtX);
                   else if (strXY.contains("onlyCY"))
                strReturn = String.valueOf( ExtY);
                   else if (strXY.contains("onlyChX"))
                strReturn = String.valueOf( ChOffX);
                   else if (strXY.contains("onlyChY"))
                strReturn = String.valueOf( ChOffY);
                   else if (strXY.contains("onlyChCX"))
                strReturn = String.valueOf( ChExtX);
                   else if (strXY.contains("onlyChCY"))
                strReturn = String.valueOf( ChExtY);

        }
       

                 return strReturn;
    }
        
    //End 	
    // added for Shadow calculation
    private String EvalShadowExpression(String text)
    {
        String[] arrVal = new String[2];
        arrVal = text.split(":");
        Double x = 0.0;
        if (arrVal.length == 3)
        {
            double arrValue1 = Double.parseDouble(arrVal[1].toString());
            double arrValue2 = Double.parseDouble(arrVal[2].toString());

            if (arrVal[0].contains("a-outerShdw-dist"))
            {
                x = Math.sqrt(arrValue1 * arrValue1 + arrValue2 * arrValue2);
                x = (double)Math.round(x * 360000);
//OpenXML Validation Error-fix
                //added by yeswanth for conformance ST_PositiveCoordinate
                if (x > 27273042316900l)
                    x = (double)27273042316900l;
            }
            if (arrVal[0].contains("a-outerShdw-dir"))
            {
                x = Math.atan(arrValue2 / arrValue1);
                x = x * (180.0 / Math.PI);
                x = (double)Math.abs(Math.round(x * 60000));
                //0 - 90 degrees
                //if (arrValue1 > 0 && arrValue2 > 0)
                //{
                //    x = Math.Atan(arrValue2 / arrValue1);
                //    x = x * (180.0 / Math.PI);
                //    x = Math.abs(Math.round(x * 60000));
                //}
                //181 - 270 degrees
                if (arrValue1 < 0 && arrValue2 < 0)
                {
                    x = Math.abs(10800000 + x);
                }
                //271 - 359 degrees
                if (arrValue1 > 0 && arrValue2 < 0)
                {
                    x = Math.abs(21600000 - x);
                }
                //91 - 180 degrees
                if (arrValue1 < 0 && arrValue2 > 0)
                {
                    x = Math.abs(10800000 - x);
                }
            }
        }
        String strRet = String.format("%.0f", x);

      
        return strRet;
    }

    //Resolve relative path to absolute path
    private String EvalHyperlinkPath(String text)
    {   
    	return "---------";
    	/*Environment.GetCommandLineArgs在java中无对应，需自己写custom utility class
        String[] arrVal = new String[1];
        arrVal = text.split(":");
        String source = arrVal[1].toString();
        String address="";
       
       
        if (arrVal.length == 2)
        {
            String returnInputFilePath = "";

            // for Commandline tool
            for (int i = 0; i < Environment.GetCommandLineArgs().Length; i++)
            {
                if (Environment.GetCommandLineArgs()[i].toString().ToUpper() == "/I")
                    returnInputFilePath = Path.GetDirectoryName(Environment.GetCommandLineArgs()[i + 1]);
            }

            //for addins
            if (returnInputFilePath == "")
            {
                returnInputFilePath = Environment.CurrentDirectory;
            }
            if (source != "")
            {
            String linkPathLocation = Path.GetFullPath(Path.Combine(returnInputFilePath, source.Remove(0, 3))).replace("/", "//").replace(" ","%20");
            address = "file:///" + linkPathLocation;
        }
        }
        return address.toString();
        */
    }
    //end


    //Image Cropping Added by Sonata-15/11/2007
    private void EvalImageCropping(String text)
    {
    	/*
        String[] arrVal = new String[6];
        arrVal = text.split(":");
        String source = arrVal[1].toString();
        double top = Double.parseDouble(arrVal[2].toString());
        double right = Double.parseDouble(arrVal[3].toString());
        double bottom = Double.parseDouble(arrVal[4].toString());
        double left = Double.parseDouble(arrVal[5].toString());


        String tempFileName = AbstractConverter.inputTempFileName.toString();
        ZipResolver zipResolverObj = new ZipResolver(tempFileName);
        OdfZipUtils.ZipArchiveWriter zipobj = new OdfZipUtils.ZipArchiveWriter(zipResolverObj);
        String widht_height_res = zipobj.ImageCopyBinary(source);
        zipResolverObj.Dispose();
        zipobj.Close();


        String[] arrValues = new String[3];
        arrValues = widht_height_res.split(":");
        double width = Double.parseDouble(arrValues[0].toString());
        double height = Double.parseDouble(arrValues[1].toString());
        double res = Double.parseDouble(arrValues[2].toString());

        double cx = width * 2.54 / res;
        double cy = height * 2.54 / res;

        int pptLeft = (int)(left * 100000 / cx);
        int pptRight = (int)(right * 100000 / cx);
        int pptTop = (int)(top * 100000 / cy);
        int pptBottom = (int)(bottom * 100000 / cy);


        WriteStartAttribute("l");
        this.WriteString(String.format("%.0f", pptLeft));
        WriteEndAttribute();
        WriteStartAttribute("r");
        this.WriteString(String.format("%.0f", pptRight));
        WriteEndAttribute();
        WriteStartAttribute("t");
        this.WriteString(String.format("%.0f", pptTop));
        WriteEndAttribute();
        WriteStartAttribute("b");
        this.WriteString(String.format("%.0f", pptBottom));
        WriteEndAttribute();
        */
    }
    //end

    //Callout Adjustments Calculation Added by Sonata
    private String EvalCalloutAdjsExpn(String text)
    {
        String[] arrVal = text.split("@");
        String[] arrValCallStr = arrVal[0].split(":");
        String[] arrCallModfr = arrVal[1].split(" ");

        int val1 =0;

        String callAdj = (arrValCallStr[0].toString());

        double CX = Double.parseDouble(arrValCallStr[1]);
        double CY = Double.parseDouble(arrValCallStr[2]);
        double X = Double.parseDouble(arrValCallStr[3]);
        double Y = Double.parseDouble(arrValCallStr[4]);
        String mirrorH = (arrValCallStr[5]);
        String mirrorV = (arrValCallStr[6]);
        double angle = Double.parseDouble(arrValCallStr[7]);

        CX = (360000.00 * CX);
        CY = (360000.00 * CY);

        X = (360000.00 * X);
        Y = (360000.00 * Y);

        int flipH;
        if (arrValCallStr[5].toString().trim().equals("true"))
        {
            flipH = 1;
        }
        else
        {
            flipH = 0;
        }

        int flipV;
        if (arrValCallStr[6].toString().trim().equals("true"))
        {
            flipV = 1;
        }
        else
        {
            flipV = 0;
        }

        double xCenter = (X + CX / 2);
        double yCenter = (Y + CY / 2);

        double xCtrBy2;
        if (flipH == 1)
        {
            xCtrBy2 = ((-1) * (CX / 2));
        }
        else
        {
            xCtrBy2 = (CX / 2);
        }

        double yCtrBy2;
        if (flipV == 1)
        {
            yCtrBy2 = ((-1) * (CY / 2));
        }
        else
        {
            yCtrBy2 = (CY / 2);
        }

        double ang;
        if (angle < 0)
        {
            ang = (-1 * angle);
        }
        else
        {
            ang = angle;
        }

           double X1;
            X1 = (xCenter - Math.cos(ang) * xCtrBy2 + Math.sin(ang) * yCtrBy2);

            double X2;
            X2 = (xCenter + Math.cos(ang) * xCtrBy2 - Math.sin(ang) * yCtrBy2);

            X1 = (double)Math.round(X1*1000 / 360000) / 1000;

            X2 = (double)Math.round(X2 * 1000 / 360000)/1000;


        double Y1;
        Y1 = (yCenter - Math.sin(ang) * xCtrBy2 - Math.cos(ang) * yCtrBy2);

        double Y2;
        Y2 = (yCenter + Math.sin(ang) * xCtrBy2 + Math.cos(ang) * yCtrBy2);

        Y1 = Math.round(Y1 *1000/ 360000)/1000.0;

        Y2 = Math.round(Y2*1000 / 360000)/1000.0;

            double width;
        width = (X1 - X2);

        double height;
        height = (Y1 - Y2);


        if ((callAdj.toString().trim().equals("Callout-DirectAdj1Notlinefmla1")) || (callAdj.toString().trim().equals("Callout-DirectAdj2Notlinefmla2")))
        {
            double viewDX = 0.0;
            double viewDY = 0.0;

            if (arrCallModfr[0] != "")
            {
                viewDX = Double.parseDouble(arrCallModfr[0]);
            }
            if (arrCallModfr[1] != "")
            {
                viewDY = Double.parseDouble(arrCallModfr[1]);
            }

            if (callAdj.toString().trim().equals("Callout-DirectAdj1Notlinefmla1"))
            {
            double viewWidth = 21600;

            double dxFinal;
            dxFinal = (width * (viewDX / viewWidth));

            double dxPos;
            dxPos = (dxFinal - width / 2);

            double fml1;
            fml1 = (dxPos / width * 100000);

            int FML1 = (int)fml1;

                val1 = FML1;
        }

            if (callAdj.toString().trim().equals("Callout-DirectAdj2Notlinefmla2"))
        {
           double viewHeight = 21600;

            double dyFinal;
            dyFinal = (height * (viewDY / viewHeight));

            double dyPos;
            dyPos = (dyFinal - height / 2);

            double fml2;
            fml2 = (dyPos / height * 100000);

            int FML2 = (int)fml2;

                val1 = FML2;
        }
        }

        if (callAdj.toString().trim().contains("Callout-DirectLine"))
        {
            double viewDX = 0.0;
            double viewDY = 0.0;
            double viewDX1 = 0.0;
            double viewDY1 = 0.0;
            double viewDX2 = 0.0;
            double viewDY2 = 0.0;
            double viewDX3 = 0.0;
            double viewDY3 = 0.0;

            if ((callAdj.toString().trim().equals("Callout-DirectLine1Adj1fmla1")) || (callAdj.toString().trim().equals("Callout-DirectLine1Adj2fmla2")) || (callAdj.toString().trim().equals("Callout-DirectLine1Adj3fmla3")) || (callAdj.toString().trim().equals("Callout-DirectLine1Adj4fmla4")))
            {
                if (!arrCallModfr[0].equals(""))
                {
                    viewDY1 = Double.parseDouble(arrCallModfr[0]);
                }
                if (!arrCallModfr[1].equals(""))
                {
                    viewDX1 = Double.parseDouble(arrCallModfr[1]);
                }
                if (!arrCallModfr[2].equals(""))
                {
                    viewDY = Double.parseDouble(arrCallModfr[2]);
                }
                if (!arrCallModfr[3].equals(""))
                {
                    viewDX = Double.parseDouble(arrCallModfr[3]);
                }
            }

            if ((callAdj.toString().trim().equals("Callout-DirectLine2Adj1fmla1")) || (callAdj.toString().trim().equals( "Callout-DirectLine2Adj2fmla2")) || (callAdj.toString().trim().equals( "Callout-DirectLine2Adj3fmla3")) || (callAdj.toString().trim().equals("Callout-DirectLine2Adj4fmla4")) || (callAdj.toString().trim().equals("Callout-DirectLine2Adj5fmla5")) || (callAdj.toString().trim().equals("Callout-DirectLine2Adj6fmla6")))
            {
                if (!arrCallModfr[0].equals(""))
                {
                    viewDY2 = Double.parseDouble(arrCallModfr[0]);
                }
                if (!arrCallModfr[1].equals(""))
                {
                    viewDX2 = Double.parseDouble(arrCallModfr[1]);
                }
                if (!arrCallModfr[2].equals(""))
                {
                    viewDY1 = Double.parseDouble(arrCallModfr[2]);
                }
                if (!arrCallModfr[3].equals(""))
                {
                    viewDX1 = Double.parseDouble(arrCallModfr[3]);
                }
                if (!arrCallModfr[4].equals(""))
                {
                    viewDY = Double.parseDouble(arrCallModfr[4]);
                }
                if (!arrCallModfr[5].equals(""))
                {
                    viewDX = Double.parseDouble(arrCallModfr[5]);
                }
            }

            if ((callAdj.toString().trim().equals("Callout-DirectLine3Adj1fmla1")) || (callAdj.toString().trim().equals("Callout-DirectLine3Adj2fmla2")) || (callAdj.toString().trim().equals("Callout-DirectLine3Adj3fmla3")) || (callAdj.toString().trim().equals("Callout-DirectLine3Adj4fmla4")) || (callAdj.toString().trim().equals("Callout-DirectLine3Adj5fmla5")) || (callAdj.toString().trim().equals("Callout-DirectLine3Adj6fmla6")) || (callAdj.toString().trim().equals("Callout-DirectLine3Adj7fmla7")) || (callAdj.toString().trim().equals("Callout-DirectLine3Adj8fmla8")))
            {
                if (!arrCallModfr[0].equals(""))
                {
                    viewDY3 = Double.parseDouble(arrCallModfr[0]);
                }
                if (!arrCallModfr[1].equals(""))
                {
                    viewDX3 = Double.parseDouble(arrCallModfr[1]);
                }
                if (!arrCallModfr[2].equals(""))
                {
                    viewDY2 = Double.parseDouble(arrCallModfr[2]);
                }
                if (!arrCallModfr[3].equals(""))
                {
                    viewDX2 = Double.parseDouble(arrCallModfr[3]);
                }
                if (!arrCallModfr[4].equals(""))
                {
                    viewDY1 = Double.parseDouble(arrCallModfr[4]);
                }
                if (!arrCallModfr[5].equals(""))
                {
                    viewDX1 = Double.parseDouble(arrCallModfr[5]);
                }
                if (!arrCallModfr[6].equals(""))
                {
                    viewDY = Double.parseDouble(arrCallModfr[6]);
                }
                if (!arrCallModfr[7].equals(""))
                {
                    viewDX = Double.parseDouble(arrCallModfr[7]);
                }
            }

            double viewWidth = 21600;
            double viewHeight = 21600;

            double vdyFinal3 = viewDY3 * 1000;
            double dyFinal3;
            dyFinal3 = (height * (vdyFinal3 / viewHeight));
            double dyPos3;
            dyPos3 = (dyFinal3 - height / 2);
            double fml8;
            fml8 = (dyPos3 / height * 100);
            int FML8 = (int)fml8;

            double vdxFinal3 = viewDX3 * 1000;
            double dxFinal3;
            dxFinal3 = (width * (vdxFinal3 / viewWidth));
            double dxPos3;
            dxPos3 = (dxFinal3 - width / 2);
            double fml7;
            fml7 = (dxPos3 / width * 100);
            int FML7 = (int)fml7;

            double vdyFinal2 = viewDY2 * 1000;
            double dyFinal2;
            dyFinal2 = (height * (vdyFinal2 / viewHeight));
            double dyPos2;
            dyPos2 = (dyFinal2 - height / 2);
            double fml6;
            fml6 = (dyPos2 / height * 100);
            int FML6 = (int)fml6;

            double vdxFinal2 = viewDX2 * 1000;
            double dxFinal2;
            dxFinal2 = (width * (vdxFinal2 / viewWidth));
            double dxPos2;
            dxPos2 = (dxFinal2 - width / 2);
            double fml5;
            fml5 = (dxPos2 / width * 100);
            int FML5 = (int)fml5;

            double vdyFinal1 = viewDY1 * 1000;
            double dyFinal1;
            dyFinal1 = (height * (vdyFinal1 / viewHeight));
            double dyPos1;
            dyPos1 = (dyFinal1 - height / 2);
            double fml4;
            fml4 = (dyPos1 / height * 100);
            int FML4 = (int)fml4;

            double vdxFinal1 = viewDX1 * 1000;
            double dxFinal1;
            dxFinal1 = (width * (vdxFinal1 / viewWidth));
            double dxPos1;
            dxPos1 = (dxFinal1 - width / 2);
            double fml3;
            fml3 = (dxPos1 / width * 100);
            int FML3 = (int)fml3;

            double vdyFinal = viewDY * 1000;
            double dyFinal;
            dyFinal = (height * (vdyFinal / viewHeight));
            double dyPos;
            dyPos = (dyFinal - height / 2);
            double fml2;
            fml2 = (dyPos / height * 100);
            int FML2 = (int)fml2;

            double vdxFinal = viewDX * 1000;
            double dxFinal;
            dxFinal = (width * (vdxFinal / viewWidth));
            double dxPos;
            dxPos = (dxFinal - width / 2);
            double fml1;
            fml1 = (dxPos / width * 100);
            int FML1 = (int)fml1;

            if (callAdj.toString().trim().equals("Callout-DirectLine3Adj8fmla8"))
            {
                val1 = FML8;
            }

            if (callAdj.toString().trim().equals("Callout-DirectLine3Adj7fmla7"))
            {
                val1 = FML7;
            }

            if ((callAdj.toString().trim().equals("Callout-DirectLine3Adj6fmla6")) || (callAdj.toString().trim().equals("Callout-DirectLine2Adj6fmla6")))
            {
                val1 = FML6;
            }

            if ((callAdj.toString().trim().equals("Callout-DirectLine3Adj5fmla5")) || (callAdj.toString().trim().equals("Callout-DirectLine2Adj5fmla5")))
            {
                val1 = FML5;
            }

            if ((callAdj.toString().trim().equals("Callout-DirectLine3Adj4fmla4")) || (callAdj.toString().trim().equals("Callout-DirectLine2Adj4fmla4")) || (callAdj.toString().trim().equals("Callout-DirectLine1Adj4fmla4")))
            {
                val1 = FML4;
            }

            if ((callAdj.toString().trim().equals("Callout-DirectLine3Adj3fmla3")) || (callAdj.toString().trim().equals("Callout-DirectLine2Adj3fmla3")) || (callAdj.toString().trim().equals("Callout-DirectLine1Adj3fmla3")))
            {
                val1 = FML3;
            }

            if ((callAdj.toString().trim().equals("Callout-DirectLine3Adj2fmla2")) || (callAdj.toString().trim().equals("Callout-DirectLine2Adj2fmla2")) || (callAdj.toString().trim().equals("Callout-DirectLine1Adj2fmla2")))
            {
                val1 = FML2;
            }

            if ((callAdj.toString().trim().equals("Callout-DirectLine3Adj1fmla1")) || (callAdj.toString().trim().equals("Callout-DirectLine2Adj1fmla1")) || (callAdj.toString().trim().equals("Callout-DirectLine1Adj1fmla1")))
            {
                val1 = FML1;
            }
        }

        return "val " + String.valueOf(val1);
    }
    //End

    public void WriteStoredRun()
    {
        Element e = (Element)this.store.peek();

        if (e instanceof Run)
        {
            Run r = (Run)e;
            if (r.hasReversedText())
            {
                isRightToLeft = true;
                SplitRun(r);
                isRightToLeft = false;
            }
            else
            { 
                if (this.store.size() < 2)
                {
                    if (r.hasText())
                    {
                        if (r.GetChild("t", NAMESPACE) != null)
                            r.ReplaceFirstTextChild(this.ReplaceSoftHyphens(r.GetChild("t", NAMESPACE).GetTextChild(), r));
                    }
                    try {
						r.Write(nextWriter);
					} catch (Exception e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
                }
            }
        }
        else
        {
            if (this.store.size() < 2)
            {
                try {
					e.Write(nextWriter);
				} catch (Exception e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
            }
        }
    }

    private void StartStoreElement()
    {
        Element element = (Element)this.currentNode.peek();

        if (this.store.size() > 0)
        {
            Element parent = (Element)this.store.peek();
            parent.AddChild(element);
        }

        this.store.push(element);
    }


    private void EndStoreElement()
    {
        Element e = (Element)this.store.pop();
    }


    private void StartStoreAttribute()
    {
        Element parent = (Element)store.peek();
        Attribute attr = (Attribute)this.currentNode.peek();
        parent.AddAttribute(attr);
        this.store.push(attr);
    }


    private void StoreString(String text)
    {
        Node node = (Node)this.currentNode.peek();

        if (node instanceof Element)
        {
            Element element = (Element)this.store.peek();
            element.AddChild(text);
        }
        //Sona: Code for Rotation
        else
        {
            Attribute attr = (Attribute)store.peek();
            if (text.contains("draw-transform"))
            {
                //char[] separator = new char[1] { ';' };
                String[] splitStr = text.split(";");
                String rotStr = "";
                String xStr = "";
                String yStr = "";
                Double angle;
                Double x=0.0;
                Double y=0.0;
                //Sona: Different culture defect fix
                String strReturn="";
                for (String str : splitStr)
                {
                    if (str.startsWith("rotation"))
                    {
                        rotStr = str.replace("rotation:", "");
                        rotStr.trim();
                        //Convert emu to degrees
                        angle = Double.parseDouble(EvalRotationExpression(rotStr)) / 60000;
                        //Sona: Different culture defect fix
                        strReturn = String.format("%.0f", angle);                            
                        text = text.replace(rotStr, strReturn);
                        
                    }
                    if (str.startsWith("margin-left"))
                    {
                        xStr = str.replace("margin-left:", "");
                        xStr.trim();
                        //Sona: Defect #1830350
                        if (xStr != "" && xStr !="0")
                        {
                        //convert emu to points
                        x = (Integer.parseInt(EvalRotationExpression(xStr)) * 72) / (360000 * 2.54);
                        //Sona: Different culture defect fix
                        strReturn = String.valueOf(Math.round(x));
                        text = text.replace(xStr, strReturn + "pt");
                    }
                        
                    }
                    if (str.startsWith("margin-top"))
                    {
                        yStr = str.replace("margin-top:", "");
                        yStr.trim();
                        //Sona: Defect #1830350
                        if (yStr != "" && yStr !="0")
                        {
                        //convert emu to points
                        y = (Integer.parseInt(EvalRotationExpression(yStr)) * 72) / (360000 * 2.54);
                        //Sona: Different culture defect fix
                        strReturn = String.valueOf(Math.round(y));
                        text = text.replace(yStr, strReturn + "pt");
                    }
                       
                    }
                }
            }
            //Start of RefNo-1
            else if (text.contains("CustShpWrdFreFrm"))
            {
                text= getSahpePath(text.substring(16));
            }
            //End of RefNo-1
            if(attr.getValue()!=null){
            	attr.setValue(attr.getValue()+text);
            }else{
            	attr.setValue(text);
            }
        }
    }


    private void EndStoreAttribute()
    {
        this.store.pop();
    }


    private void SplitRun(Run r)
    {
        Run extractedRun = new Run("w", "r", NAMESPACE);
        Element rPr = r.GetChild("rPr", NAMESPACE);
        Element rPr0 = null;

        if (rPr != null)
        {
            rPr0 = rPr.Clone();
        }
        else
        {
            rPr0 = new Element("w", "rPr", NAMESPACE);
        }

        // get first subString of run of a unique type, and retrieve it from run
        TextProperties extractedText = ExtractText(r);
        if (extractedText.isReverse())
        {
            rPr0.AddChild(new Element("w", "rtl", NAMESPACE));
            if (rPr0.GetChild("b", NAMESPACE) != null && rPr0.GetChild("b", NAMESPACE).GetAttribute("val", NAMESPACE).getValue().equals("on"))
            {
                Element elt = new Element("w", "bCs", NAMESPACE);
                elt.AddAttribute(new Attribute("w", "val", "on", NAMESPACE));
                rPr0.AddChild(elt);
            }
            if (rPr0.GetChild("i", NAMESPACE) != null && rPr0.GetChild("i", NAMESPACE).GetAttribute("val", NAMESPACE).getValue().equals("on"))
            {
                Element elt = new Element("w", "iCs", NAMESPACE);
                elt.AddAttribute(new Attribute("w", "val", "on", NAMESPACE));
                rPr0.AddChild(elt);
            }
            String fontSize = null;
            if (rPr0.GetChild("sz", NAMESPACE) != null)
                fontSize = rPr0.GetChild("sz", NAMESPACE).GetAttribute("val", NAMESPACE).getValue();
            if (fontSize != null)
            {
                Element elt = new Element("w", "szCs", NAMESPACE);
                elt.AddAttribute(new Attribute("w", "val", fontSize, NAMESPACE));
                rPr0.AddChild(elt);
            }
            rPr0 = this.GetOrderedRunProperties(rPr0);
        }

        extractedRun.AddChild(rPr0);
        Element t = new Element("w", "t", NAMESPACE);
        t.AddAttribute(new Attribute("xml", "space", "preserve", null));
        t.AddChild(extractedText.getContent());
        extractedRun.AddChild(t);

        if (this.store.size() < 2)
        {
            try {
				extractedRun.Write(nextWriter);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        }
        else
        {
            Element parent = GetParent(r, this.store);
            if (parent != null)
            {
                parent.AddChild(extractedRun);
            }
        }

        // Check the rest of the content : Run r now contains the remaining text.
        if (r.hasReversedText() || r.hasPonctuation())
        {
                SplitRun(r);
        }
        else
        {
            if (this.store.size() < 2)
            {
                if (r.hasNotEmptyText())
                {
                    try {
						r.Write(nextWriter);
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
                }
                else
                {
                    boolean hasRelevantChild = false;
                    //try to avoid empty runs
                    for (Element runChild : r.GetChildElements())
                    {
                        if ("t".equals(runChild.getName()) && NAMESPACE.equals(runChild.getNs())) { }
                        else if ("rPr".equals(runChild.getName()) && NAMESPACE.equals(runChild.getNs())) { }
                        else
                        {
                            hasRelevantChild = true;
                            break;
                        }
                    }
                    if (hasRelevantChild)
                        //do not write run, just pop it.
                        this.currentNode.pop();
                }
            }
        }

    }

    // Extract the first part of run text that instanceof of one particular type
    private TextProperties ExtractText(Run r)
    {
        TextProperties extractedText = new TextProperties();
        // teake the very first String of run.
        String text = r.GetChild("t", NAMESPACE).GetTextChild();
        int startChar = -1;
        int endChar = -1;
        int startHebrew = -1;
        int startArabic = -1;
        int startExtendedArabic = -1;
        char[] charTable = null;

        // get start of special symbol substring
        if ((startHebrew = StringUtils.indexOfAny(text,HEBREW_SYMBOLS)) > -1)
        {
            startChar = startHebrew;
            charTable = HEBREW_SYMBOLS;
        }
        if ((startArabic = StringUtils.indexOfAny(text,ARABIC_BASIC_SYMBOLS)) > -1)
        {
            if (startChar > -1)
                startChar = Math.min(startChar, startArabic);
            else startChar = startArabic;
            if (startChar==startArabic)
                charTable = ARABIC_BASIC_SYMBOLS;
        }
        if ((startExtendedArabic = StringUtils.indexOfAny(text,ARABIC_EXTENDED_SYMBOLS)) > -1)
        {
            if (startChar > -1)
                startChar = Math.min(startChar, startExtendedArabic);
            else startChar = startExtendedArabic;
            if (startChar==startExtendedArabic)
                charTable = ARABIC_EXTENDED_SYMBOLS;
        }

        if (startChar==0) // isolate right to left words
        {
            // get end of special symbol substring
            endChar = this.GetLastIndexOfCharSetInString(text, startChar, charTable);
            // retrieve subString from run
            r.ReplaceFirstTextChild(text.substring(endChar + 1, text.length() - endChar - 1));
            // return substring. Do not apply character processing to special characters.
            extractedText.setContent(text.substring(0, endChar + 1));
            extractedText.setReverse(true);
        }
        else if (startChar > 0) // all the rest : left to right words, spaces and ponctuation.
        {
            String remaining = text.substring(startChar, text.length() - startChar);
            if (text.indexOf('\u0020')==0 || StringUtils.indexOfAny(text,PONCTUATION)==0)
            {
                extractedText.setContent( text.substring(0, 1));
                r.ReplaceFirstTextChild(text.substring(1, text.length() - 1));
                extractedText.setReverse(isRightToLeft);
            }
            else
            {
                String begining = text.substring(0, startChar);
                // remove trailing spaces and ponctuation from the begining string.
                
                // text ending with ponctuation of space
                if (begining.indexOf('\u0020')==(begining.length()-1) || StringUtils.indexOfAny(begining,PONCTUATION)==(begining.length() - 1)) 
                {
                    begining = TrimPonctuation(begining);
                    r.ReplaceFirstTextChild(text.substring(begining.length(), text.length() - begining.length()));
                    extractedText.setContent(this.ReplaceSoftHyphens(begining, r));
                    extractedText.setReverse(false);
                }
                else 
                {
                    // retrieve subString from run
                    r.ReplaceFirstTextChild(remaining);
                    // return first substring
                    extractedText.setContent( this.ReplaceSoftHyphens(begining, r));
                    extractedText.setReverse(false);
                }
            }

        }
        else // text that does not have right to left text
        {
            // begining with space or ponctuation => must be cut : two runs
            if (text.indexOf('\u0020') == 0 || StringUtils.indexOfAny(text,PONCTUATION) == 0)
            {
            	extractedText.setContent(  text.substring(0, 1));
                r.ReplaceFirstTextChild(text.substring(1, text.length() - 1));
                extractedText.setReverse(isRightToLeft);
            }
            // ending with space or ponctuation => must be cut : two runs
            else if (text.length() > 0 && (text.indexOf('\u0020')==(text.length() - 1) ||
            		StringUtils.indexOfAny(text.substring(text.length() - 1, 1),PONCTUATION) == 0))
            {
                String begining = TrimPonctuation(text);
                r.ReplaceFirstTextChild(text.substring(begining.length(), text.length() - begining.length()));
                extractedText.setContent(  this.ReplaceSoftHyphens(begining, r));
                extractedText.setReverse(false);
            }
            else
            {
                // text that does not begin or end with ponctiuation or space
            	extractedText.setContent(  this.ReplaceSoftHyphens(text, r));
            	extractedText.setReverse(false);
            }
        }

        return extractedText;
    }


    // Find the last character of a certain char-set : a string.
    private int GetLastIndexOfCharSetInString(String text, int startIndex, char[] charTable)
    {
        int i = startIndex;
        while (i < text.length() && this.IsContainedInCharTable((text.substring(i, 1)), charTable))
        {
            i++;
        }
        return i-1;
    }

    public String TrimPonctuation(String text)
    {
        String res = text;
        String lastChar = res.substring(res.length() - 1, 1);
        while (res.length() > 0 && (lastChar.indexOf('\u0020') == 0 || StringUtils.indexOfAny(lastChar,PONCTUATION) == 0))
        {
            res = res.substring(0, res.length() - 1);
            lastChar = res.substring(res.length() - 1, 1);
        }
        return res;
    }


    // Check if a character instanceof contained : a char-set table
    private boolean IsContainedInCharTable(String character, char[] charTable)
    {
        return StringUtils.indexOfAny(character,charTable) >= 0;
    }


    private String ReplaceSoftHyphens(String text, Run r)
    {
        String subString = "";
        int i = 0;
        if ((i = text.indexOf('\u00AD')) >= 0)
        {
            subString = this.ReplaceNonBreakingHyphens(text.substring(0, i), r);
            r.AddChild(new Element("w", "softHyphen", NAMESPACE));
            if (i < text.length() - 1)
            {
                Element newT = new Element("w", "t", NAMESPACE);
                newT.AddChild(this.ReplaceSoftHyphens(text.substring(i + 1, text.length() - i - 1), r));
                r.AddChild(newT);
            }
        }
        else
        {
            subString = this.ReplaceNonBreakingHyphens(text, r);
        }
        return subString;
    }


    private String ReplaceNonBreakingHyphens(String text, Run r)
    {
        String subString = "";
        int i = 0;
        if ((i = text.indexOf('\u2011')) >= 0)
        {
            subString = text.substring(0, i);
            r.AddChild(new Element("w", "noBreakHyphen", NAMESPACE));
            if (i < text.length() - 1)
            {
                Element newT = new Element("w", "t", NAMESPACE);
                newT.AddChild(this.ReplaceNonBreakingHyphens(text.substring(i + 1, text.length() - i - 1), r));
                r.AddChild(newT);
            }
        }
        else
        {
            subString = text;
        }
        return subString;
    }

    /// <summary>
    /// Returns true if the current node contains a date.
    /// 
    /// This instanceof needed to undo the special post-processing done for XSD times : DOCX->ODT conversion
    /// (ODF only accepts the format YYYY-MM-DDThh:mm:ss without trailing TZD, see OdfCharactersPostProcessor).
    /// 
    /// Additionaly, OOo interpretes times as local times -> we convert from UTC to local time 
    /// (Of course this only gives the correct value if the document instanceof converted : the same time zone 
    /// for which the time value has been written, but it instanceof better than nothing). This allows to keep e.g. the 
    /// correct value for CREATIONDATE fields when not moving within time zones
    /// </summary>
    /// <returns></returns>
    private boolean IsDate()
    {
        if (this.currentNode.size() > 0)
        {
        	Node node = (Node) this.currentNode.peek();
        	Element element = null;
        	if (node instanceof Element){
        		element = (Element)node;
        	}

            if (element != null
                && (element.getPrefix().equals("dcterms") && (element.getName().equals("created") || element.getName().equals("modified"))
                    || element.getPrefix().equals("cp") && element.getName().equals("lastPrinted")))
            {
                return true;
            }
        }
        return false;
    }

    private boolean IsRun()
    {
        Node node = (Node)this.currentNode.peek();
        if (node instanceof Element)
        {
            Element element = (Element)node;
            if ("r".equals(element.getName()) && NAMESPACE.equals(element.getNs()))
            {
                return true;
            }
        }
        return false;
    }


    private boolean InRun()
    {
        return IsRun() || this.store.size() > 0;
    }

    private Element GetParent(Element e, Stack stack)
    {
    	Iterator objEnum = stack.iterator();
        while (objEnum.hasNext())
        {
            Element parent = (Element)objEnum.next();
            if (parent != null)
            {
                for (Object child : parent.getChildren())
                {
                    if (child == e) // object identity
                    {
                        return parent;
                    }
                }
            }
        }
        return null;
    }

    protected class Run extends Element
    {
        private TextProperties TextProperties;

        public TextProperties getTextProperties() {
			return TextProperties;
		}

		public void setTextProperties(TextProperties textProperties) {
			TextProperties = textProperties;
		}

		public Run(Element e)  { 
        	super(e.getPrefix(), e.getName(), e.getNs());
        }

        public Run(String prefix, String localName, String ns) {
        	super(prefix, localName, ns);
        }

        public boolean hasReversedText()
        {
            return HasReversedTextNode(this);
        }

        public boolean hasPonctuation()
        {
            return HasPonctuationNode(this);
        }

        private boolean HasReversedTextNode(Element e)
        {
            boolean b = false;
            for (Object node : e.getChildren())
            {
                if (node instanceof Element)
                {
                    Element element = (Element)node;
                    if (element.GetTextChild() != null)
                    {
                        b = b || isReversed(element.GetTextChild());
                    }
                }
            }
            return b;
        }

        private boolean HasPonctuationNode(Element e)
        {
            boolean b = false;
            for (Object node : e.getChildren())
            {
                if (node instanceof Element)
                {
                    Element element = (Element) node;
                    if (element.GetTextChild() != null)
                    {
                        b = b || hasPonctuation(element.GetTextChild());
                    }
                }
            }
            return b;
        }

        private boolean hasPonctuation(String text)
        {
            if (text.indexOf('\u0020') == 0)
                return true;
            else if (StringUtils.indexOfAny(text, PONCTUATION) == 0)
                return true;
            else if (text.length() > 0) 
            {
                String lastChar = text.substring(text.length() - 1, 1);
                if (lastChar.lastIndexOf('\u0020') == 0)
                    return true;
                else if (StringUtils.indexOfAny(lastChar, PONCTUATION) == 0)
                    return true;
                else return false;
            }
            else return false;
        }

        private boolean isReversed(String text)
        {
            if (StringUtils.indexOfAny(text, HEBREW_SYMBOLS) >= 0)
                return true;
            else if (StringUtils.indexOfAny(text,ARABIC_BASIC_SYMBOLS) >= 0)
                return true;
            else if (StringUtils.indexOfAny(text,ARABIC_EXTENDED_SYMBOLS) >= 0)
                return true;
            else return false;
        }

        public void ReplaceFirstTextChild(String newText)
        {
            if (this.GetChild("t", NAMESPACE) != null)
            {
                String oldText = this.GetChild("t", NAMESPACE).GetTextChild();
                if (!oldText.equals(newText))
                {
                    this.GetChild("t", NAMESPACE).Replace(oldText, newText);
                }
            }
        }

        public boolean hasNotEmptyText()
        {
            return HasNotEmptyTextNode(this);
        }


        private boolean HasNotEmptyTextNode(Element e)
        {
            boolean b = false;
            for (Object node : e.getChildren())
            {
                if (node instanceof Element)
                {
                    Element element = (Element)node;
                    if (element.GetTextChild() != null && element.GetTextChild().length() > 0)
                    {
                        b = true;
                    }
                }
            }
            return b;
        }

        public boolean hasText()
        {
            return HasTextNode(this);
        }


        private boolean HasTextNode(Element e)
        {
            boolean b = false;
            if ("t".equals(e.getName()) && NAMESPACE.equals(e.getNs()))
            {
                b = true;
            }
            else
            {

                for (Object node : e.getChildren())
                {
                    if (node instanceof Element)
                    {
                        b = b || HasTextNode((Element)node);
                    }
                    else
                    {
                        b = true;
                    }
                }
            }
            return b;
        }

    }

    protected class TextProperties
    {
        private boolean isReverse;
        private String content;
        

        public boolean isReverse() {
			return isReverse;
		}
		public void setReverse(boolean isReverse) {
			this.isReverse = isReverse;
		}
		public String getContent() {
			return content;
		}
		public void setContent(String content) {
			this.content = content;
		}
    }

    private Element GetOrderedRunProperties(Element rPr)
    {
        Element newRPr = new Element(rPr);
        for (String propName : RUN_PROPERTIES)
        {
            Element prop = rPr.GetChild(propName, NAMESPACE);
            if (prop != null)
            {
                newRPr.AddChild(prop);
            }
        }
        return newRPr;
    }

    //Start of RefNo-1
    private String getSahpePath(String strOdfEnhancedPath)
    {
        //adj$modifiers,'###eqn',$drawEqn,'###box',$viewBox,'###path',$enhPath
        
            String[] stringSeparators = new String[] { "###" };
            String[] strArrShpPth = strOdfEnhancedPath.split(stringSeparators[0]);
            //, StringSplitOptions.None,不知是否影响
            char[] arrOperators = { '*', '/', '+', '-', '(', ')',','};

            String strModifiers = strArrShpPth[0].trim();
            String strDrawEqn = strArrShpPth[1].trim();
            String strViewBox = strArrShpPth[2].trim();
            String strEnhPath = strArrShpPth[3].trim();

           String[] strArrAdj = strModifiers.split(" ");

            String[] strArrBox = strViewBox.split(" ");

            if (strArrBox.length == 4)
            {
                //replace L T R B. W H
                strDrawEqn = strDrawEqn.replace("left", strArrBox[0].trim());
                strDrawEqn = strDrawEqn.replace("top", strArrBox[1].trim());
                strDrawEqn = strDrawEqn.replace("right", strArrBox[2].trim());
                strDrawEqn = strDrawEqn.replace("bottom", strArrBox[3].trim());
                strDrawEqn = strDrawEqn.replace("width", strArrBox[2].trim());
                strDrawEqn = strDrawEqn.replace("height", strArrBox[3].trim());
                //TO DO : replace 'xstretch'|'ystretch'|'hasstroke'|'hasfill'|'logwidth'|'logheight'
            }

            String[] strArrEqn = strDrawEqn.split("|");
            String[] strEqnSlvd = { "" };
            String strFinalPath = "";
            
            Pattern p = Pattern.compile("[M]\b");
            Matcher m = p.matcher(strEnhPath);
            strEnhPath = m.replaceAll("m");

            strEnhPath = regexReplace(strEnhPath, "[M]\b", "m");
            strEnhPath = regexReplace(strEnhPath, "[L]\b", "l");
            strEnhPath = regexReplace(strEnhPath, "[C]\b", "c");
            strEnhPath = regexReplace(strEnhPath, "[Z]\b", "x");
            strEnhPath = regexReplace(strEnhPath, "[N]\b", "e");
            // Vipul:
            strEnhPath = regexReplace(strEnhPath, "[F]\b", "");
            strEnhPath = regexReplace(strEnhPath, "[S]\b", "ns");
            strEnhPath = regexReplace(strEnhPath, "[A]\b", "at");
            strEnhPath = regexReplace(strEnhPath, "[T]\b", "ae");
            strEnhPath = regexReplace(strEnhPath, "[U]\b", "al");
            strEnhPath = regexReplace(strEnhPath, "[B]\b", "ar");
            strEnhPath = regexReplace(strEnhPath, "[W]\b", "wa");
            strEnhPath = regexReplace(strEnhPath, "[V]\b", "wr");
            strEnhPath = regexReplace(strEnhPath, "[Q]\b", "qb");
            strEnhPath = regexReplace(strEnhPath, "[X]\b", "qx");
            strEnhPath = regexReplace(strEnhPath, "[Y]\b", "qy");

            strEnhPath = strEnhPath.replace("  ", " ");    
            String[] strArrPath = strEnhPath.split(" ");

            if (strEnhPath != "" && (strEnhPath.contains("$") || strEnhPath.contains("?")))
            {
                strEqnSlvd = getEquationVals(strArrAdj, strArrEqn);
            }
            for (int intPathCnt = 0; intPathCnt < strArrPath.length; intPathCnt++)
            {
                String strElement = strArrPath[intPathCnt].toString().trim();
                if ((regexIsMatch(strArrPath[intPathCnt].toString().trim(), "[^0-9.]")))
                {
                    if (strElement.startsWith("$"))
                    {
                        strFinalPath = strFinalPath +
                            String.valueOf(Math.round(
                                Double.parseDouble(
                                    strArrAdj[Integer.parseInt(stringRemove(strElement,0, 1))].trim())
                                        )) + ",";
                    }
                    else if (strElement.startsWith("?"))
                    {
                        while (strElement.contains("?"))
                        {
                            strElement = strEqnSlvd[Integer.parseInt(stringRemove(strElement,0, 2))];

                           if (strElement.contains("if"))
                           {
                               strElement = GetIfCmdVal(strElement, strEqnSlvd);
                           }
                           else if (strElement.contains("?") && StringUtils.indexOfAny(strElement,arrOperators) >= 0)
                           {
                               strElement = GetEqnVal(strElement, strEqnSlvd);
                           }
                            if (strElement.trim().startsWith("-"))
                            {
                                strElement = "(" + strElement + ")";
                            }
                        }
                        strElement = strElement.replace("(", "").replace(")", "").trim();
                        strElement = String.valueOf(Math.round(Double.parseDouble(strElement)));
                        strFinalPath = strFinalPath + strElement + ",";
                    }
                    else
                    {
                        if (strFinalPath.endsWith(","))
                        {
                            strFinalPath = strFinalPath.substring(0, strFinalPath.length() - 1) + strElement;
                        }
                        else
                        {
                            strFinalPath = strFinalPath + strElement;
                        }
                    }
                }
                else
                {
                    strFinalPath = strFinalPath + strElement + ",";
                }
            }
            return strFinalPath;
    }
    
    private String regexReplace(String text, String pattern, String toreplace)
    {
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(text);
        return m.replaceAll(toreplace);        
    }
    
    private boolean regexIsMatch(String text, String pattern)
    {
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(text);
        return m.find();  	
    }
    
    private String stringRemove(String text, int startIndex, int count)
    {
    	String textTemp = text;
    	int l = text.length();
    	if(startIndex>0){
    		if(startIndex+count<l){
    			return textTemp.substring(0,startIndex)+ text.substring(startIndex+count,l);
    		}else{
    			return textTemp.substring(0,startIndex);
    		}
    	}else{
    		return textTemp.substring(count);
    	}
    }

    private String[] getEquationVals(String[] strArrAdj, String[] strArrEqn)
    {
        char[] arrOperators = { '*', '/', '+', '-', '(', ')',','};
        String[] strArrEqnResult = new String[strArrEqn.length];

        for (int intCnt = 0; intCnt < strArrEqn.length; intCnt++)
        {
            ArrayList arlEqnPrts = new ArrayList();
            ArrayList arlEqnRplPrts = new ArrayList();
            String strCurEqn = strArrEqn[intCnt].trim();
            String[] strArrExpElements = stringSplitByArray(strCurEqn,arrOperators);//, StringSplitOptions.RemoveEmptyEntries
            
            for (int i = 0; i < strArrExpElements.length; i++)
            {
                if (regexIsMatch(strArrExpElements[i].toString().trim(), "[^0-9.]"))
                {
                    String strElement = strArrExpElements[i].toString().trim();                        
                    if(strElement.startsWith("$"))
                    {
                        if (!arlEqnPrts.contains(strElement))
                        {
                            if (Integer.parseInt(stringRemove(strElement,0,1)) < strArrAdj.length)
                            {
                                String strEqnRpl = strArrAdj[Integer.parseInt(stringRemove(strElement,0,1))].trim();
                                if (strEqnRpl.contains("-"))
                                {
                                    arlEqnRplPrts.add("("+strEqnRpl+")");
                                }
                                else
                                {
                                    arlEqnRplPrts.add(strEqnRpl);
                                }
                            }
                            else
                            {
                                arlEqnRplPrts.add("0");
                            }
                            arlEqnPrts.add(strElement);
                        }
                    }
                    else if (strElement.startsWith("?"))
                    {
                        if (!arlEqnPrts.contains(strElement))
                        {
                            if (Integer.parseInt(stringRemove(strElement,0, 2)) < strArrEqnResult.length)
                            {
                                if (Integer.parseInt(stringRemove(strElement,0, 2)) < intCnt)
                                {
                                    String strEqnRpl = strArrEqnResult[Integer.parseInt(stringRemove(strElement,0, 2))];
                                    if (strEqnRpl.contains("-"))
                                    {
                                        arlEqnRplPrts.add("(" + strEqnRpl + ")");
                                    }
                                    else
                                    {
                                        arlEqnRplPrts.add(strEqnRpl);
                                    }
                                }
                                else
                                {
                                    arlEqnRplPrts.add(strElement);
                                }
                            }
                            else
                            {
                                arlEqnRplPrts.add("0");
                            }
                            arlEqnPrts.add(strElement);
                        }
                    }                      
                }                   
            }

            for (int intReplace = 0; intReplace < arlEqnPrts.size(); intReplace++)
            {
                strCurEqn = regexReplace(strCurEqn, arlEqnPrts.get(intReplace).toString().replace("$","\\$").replace("?","\\?") + "\\b", arlEqnRplPrts.get(intReplace).toString());
            }
            if (strCurEqn.contains("if"))
            {
                strArrEqnResult[intCnt] = resolveIfCmd(strCurEqn);
            }
            else
            {
                if (strCurEqn.contains("?"))
                {
                    strArrEqnResult[intCnt] = strCurEqn;
                }
                else
                {
                    if (strCurEqn.startsWith("*"))
                    {
                        strArrEqnResult[intCnt] = Evaluate(strCurEqn.substring(1));
                    }
                    else
                    {
                        strArrEqnResult[intCnt] = Evaluate(strCurEqn);
                    }
                }
                
            }         
        }
        return strArrEqnResult;
    }

    public static String[] stringSplitByArray(String text, char[] aSep)
    {//split,参数为一字符数组
    	String textTemp = text;
    	ArrayList<String> result = new ArrayList<String>();
    	int iIndex;
    	iIndex = StringUtils.indexOfAny(textTemp, aSep);
    	while(iIndex!=-1){
			if(iIndex==0){
                textTemp = textTemp.substring(1);
    		    iIndex = StringUtils.indexOfAny(textTemp, aSep);
			}else{
    		result.add(textTemp.substring(0,iIndex));
    		textTemp = textTemp.substring(iIndex);
    		iIndex = StringUtils.indexOfAny(textTemp, aSep);
			}
    	}
    	result.add(textTemp);
    	int n = result.size();
    	if(n>0){
    		String[] r = new String[n];
    		for(int i=0;i<n;i++){
    			r[i]= result.get(i);
    		}
    		return r;
    	}
    	return new String[]{};
    }
    private String resolveIfCmd(String strIfCmd)
    {
        String[] strIf = strIfCmd.substring(3, strIfCmd.length() - 4).split(",");
        String strIfChk = strIf[0].replace("(", "").replace(")", "").trim();
        String strResult = "";

        if (strIfChk.contains("?"))
        {
            strResult = strIfCmd;
        }
        else
        {
            if (Double.parseDouble(strIfChk) > 0)
            {
                strResult = strIf[1].trim();
            }
            else
            {
                strResult = strIf[2].trim();
            }
        }
        return strResult;
    }

    private String GetIfCmdVal(String strIfCmd, String[] strEqnSlvd)
    {
        String[] strIf = strIfCmd.substring(3, strIfCmd.length() - 4).split(",");
        String strIfChk = strIf[0].replace("(", "").replace(")", "").trim();
        String strResult = strIfChk;

        if (strIfChk.contains("?"))
        {
            do
            {
                strResult = strEqnSlvd[Integer.parseInt(stringRemove(strResult,0, 2))];
                
                while (strResult.contains("if"))
                {
                    strResult = GetIfCmdVal(strResult, strEqnSlvd);
                    
                    while (strResult.contains("?"))
                    {
                        strResult = strEqnSlvd[Integer.parseInt(stringRemove(strResult,0, 2))];
                    }
                }
            }
            while (strResult.contains("?"));
        }
        if (Double.parseDouble(strResult.trim().replace("(", "").replace(")", "")) > 0)
        {
            strResult = strIf[1].trim();
        }
        else
        {
            strResult = strIf[2].trim();
        }
        return strResult.trim();
    }

    private String GetEqnVal(String strEqn, String[] strEqnSlvd)
    {
        char[] arrOperators = { '*', '/', '+', '-', '(', ')', ',' };
        ArrayList arlEqnPrts = new ArrayList();
        ArrayList arlEqnRplPrts = new ArrayList();
        String strCurEqn = strEqn;
        String strResult = "";
        String[] strArrExpElements = stringSplitByArray(strCurEqn,arrOperators);//, StringSplitOptions.RemoveEmptyEntries
        for (int i = 0; i < strArrExpElements.length; i++)
        {
            if (regexIsMatch(strArrExpElements[i].toString().trim(), "[^0-9.]"))
            {
                String strElement = strArrExpElements[i].toString().trim();
                if (strElement.startsWith("?"))
                {
                    if (!arlEqnPrts.contains(strElement))
                    {
                        if (Integer.parseInt(stringRemove(strElement,0, 2)) < strEqnSlvd.length)
                        {
                            String strEqnRpl = strEqnSlvd[Integer.parseInt(stringRemove(strElement,0, 2))];
                            if (strEqnRpl.contains("-"))
                            {
                                 arlEqnRplPrts.add("(" + strEqnRpl + ")");
                            }
                            else
                            {
                                arlEqnRplPrts.add(strEqnRpl);
                            }                                
                        }
                        else
                        {
                            arlEqnRplPrts.add("0");
                        }
                        arlEqnPrts.add(strElement);
                    }
                }
            }
        }

        for (int intReplace = 0; intReplace < arlEqnPrts.size(); intReplace++)
        {
            strCurEqn = regexReplace(strCurEqn, arlEqnPrts.get(intReplace).toString().replace("$", "\\$").replace("?", "\\?") + "\\b", arlEqnRplPrts.get(intReplace).toString());
        }
        if (strCurEqn.contains("if"))
        {
            strResult = resolveIfCmd(strCurEqn);
        }
        else
        {
            if (strCurEqn.contains("?"))
            {
                strResult = strCurEqn;
            }
            else
            {
                strResult = Evaluate(strCurEqn);
            }
        }
        return strResult;
    }      

    private String Evaluate(String strEquation)
    {
        String strResult = "dddssssss";   
        /*
        Eval evl = new Eval();            
        evl.ParseEquation(strEquation);
        evl.Convert2PostFix();
        evl.EvaluatePostfix();
        if (evl.Error)
        {
            strResult = "0";
        }
        strResult = evl.Result.ToString(System.Globalization.CultureInfo.InvariantCulture);   
        */             
        return strResult;
    }      
    //End of RefNo-1	
	
	
}
